import chisel3._
import chisel3.util._

object MaskData {
  def apply(old_data: UInt, new_data: UInt, mask: UInt) = {
    (new_data & mask) | (old_data & ~mask)
  }
}

object MaskExpand {
  def apply(x: UInt) = Cat(x.asBools.map(Fill(8, _)).reverse)
}

object BoolStopWatch {
  def apply(start: Bool, stop: Bool, start_high_priority: Boolean = false) = {
    val r = RegInit(false.B)
    if (start_high_priority) {
      when (stop) { r := false.B }
      when (start) { r := true.B }
    }
    else {
      when (start) { r := true.B }
      when (stop) { r := false.B }
    }
    r
  }
} 

object HoldUnless {
  def apply[T <: Data](x: T, en: Bool): T = {
    Mux(en, x, RegEnable(x, 0.U.asTypeOf(x), en))
  }
}

object ID {
 /*cache */
  val InstCacheId = 1
  val DataCacheId = 2
  val InstUncacheId = 3
  val DataUncacheId = 4

  val ClintAddrBase = "h02000000".U
  val ClintAddrSize = "h00010000".U

  val TargetOscpuSoc = false

 /* instruction type */
  val InstrN = "b0000".U
  val InstrI = "b0100".U
  val InstrR = "b0101".U
  val InstrS = "b0010".U
  val InstrB = "b0001".U
  val InstrU = "b0110".U
  val InstrJ = "b0111".U

  /* src1 type */
  val Src1Reg = "b00".U
  val Src1Pc  = "b01".U
  val Src1Imm = "b10".U
  /* src2 type */
  val Src2Imm = "b00".U
  val Src2Reg = "b01".U

  /* function unit type */
  val FuAlu = "b000".U
  val FuBru = "b001".U
  val FuLsu = "b010".U
  val FuCsr = "b011".U
  val FuMou = "b100".U

  /* ALU operation type */
  val AluAdd  = "b000000".U
  val AluSll  = "b000001".U
  val AluSlt  = "b000010".U
  val AluSltu = "b000011".U
  val AluXor  = "b000100".U
  val AluSrl  = "b000101".U
  val AluOr   = "b000110".U
  val AluAnd  = "b000111".U
  val AluSub  = "b001000".U
  val AluSra  = "b001101".U
  val AluLui  = "b001111".U
  val AluAddiw= "b010000".U
  val AluSraw = "b010001".U  
  val AluSlli = "b010010".U    
  val AluSrli = "b010011".U  
  val AluSrai = "b010100".U  
  val AluSllw = "b010101".U 
  val AluSrlw = "b010110".U
  val AluSubw = "b010111".U  

  /* BRU operation type */
  val BruJal  = "b011000".U
  val BruJalr = "b011001".U
  val BruBeq  = "b011010".U
  val BruBne  = "b011011".U
  val BruBlt  = "b011100".U
  val BruBge  = "b011101".U
  val BruBltu = "b011110".U
  val BruBgeu = "b011111".U

  /* LSU operation type */
  val LsuLb   = "b100000".U
  val LsuLh   = "b100001".U
  val LsuLw   = "b100010".U
  val LsuLd   = "b100011".U
  val LsuLbu  = "b100100".U
  val LsuLhu  = "b100101".U
  val LsuLwu  = "b100110".U
  val LsuSb   = "b101000".U
  val LsuSh   = "b101001".U
  val LsuSw   = "b101010".U
  val LsuSd   = "b101011".U

  val CsrW    = "b101100".U
  val CsrS    = "b101101".U
  val CsrC    = "b101110".U  
  val CsrWi   = "b101111".U
  val CsrSi   = "b110000".U
  val CsrCi   = "b110001".U  
  val CsrJmp  = "b110010".U

  val Fencei  = "b110011".U

  /* instruction pattern */
  val ADDI    = BitPat("b????????????_?????_000_?????_0010011")
  val SLLI    = BitPat("b000000??????_?????_001_?????_0010011")
  val SLTI    = BitPat("b????????????_?????_010_?????_0010011")  
  val SLTIU   = BitPat("b????????????_?????_011_?????_0010011")
  val XORI    = BitPat("b????????????_?????_100_?????_0010011")
  val SRLI    = BitPat("b000000??????_?????_101_?????_0010011")
  val ANDI    = BitPat("b????????????_?????_111_?????_0010011")
  val SRAI    = BitPat("b010000??????_?????_101_?????_0010011")
  val ORI     = BitPat("b????????????_?????_110_?????_0010011")

  val ADD     = BitPat("b0000000_?????_?????_000_?????_0110011")
  val SLL     = BitPat("b0000000_?????_?????_001_?????_0110011")
  val SLT     = BitPat("b0000000_?????_?????_010_?????_0110011")
  val SLTU    = BitPat("b0000000_?????_?????_011_?????_0110011")
  val XOR     = BitPat("b0000000_?????_?????_100_?????_0110011")
  val SRL     = BitPat("b0000000_?????_?????_101_?????_0110011")
  val OR      = BitPat("b0000000_?????_?????_110_?????_0110011")
  val AND     = BitPat("b0000000_?????_?????_111_?????_0110011")
  val SUB     = BitPat("b0100000_?????_?????_000_?????_0110011")
  val SRA     = BitPat("b0100000_?????_?????_101_?????_0110011")

  val AUIPC   = BitPat("b????????????????????_?????_0010111")
  val LUI     = BitPat("b????????????????????_?????_0110111")

  val ADDIW   = BitPat("b???????_?????_?????_000_?????_0011011")
  val SLLIW   = BitPat("b0000000_?????_?????_001_?????_0011011")
  val SRLIW   = BitPat("b0000000_?????_?????_101_?????_0011011")
  val SRAIW   = BitPat("b0100000_?????_?????_101_?????_0011011")
  val SLLW    = BitPat("b0000000_?????_?????_001_?????_0111011")
  val SRLW    = BitPat("b0000000_?????_?????_101_?????_0111011")
  val SRAW    = BitPat("b0100000_?????_?????_101_?????_0111011")
  val ADDW    = BitPat("b0000000_?????_?????_000_?????_0111011")
  val SUBW    = BitPat("b0100000_?????_?????_000_?????_0111011")

  val JAL     = BitPat("b????????????????????_?????_1101111")
  val JALR    = BitPat("b????????????_?????_000_?????_1100111")

  val BNE     = BitPat("b???????_?????_?????_001_?????_1100011")
  val BEQ     = BitPat("b???????_?????_?????_000_?????_1100011")
  val BLT     = BitPat("b???????_?????_?????_100_?????_1100011")
  val BGE     = BitPat("b???????_?????_?????_101_?????_1100011")
  val BLTU    = BitPat("b???????_?????_?????_110_?????_1100011")
  val BGEU    = BitPat("b???????_?????_?????_111_?????_1100011")

  val LB      = BitPat("b????????????_?????_000_?????_0000011")
  val LH      = BitPat("b????????????_?????_001_?????_0000011")
  val LW      = BitPat("b????????????_?????_010_?????_0000011")
  val LBU     = BitPat("b????????????_?????_100_?????_0000011")
  val LHU     = BitPat("b????????????_?????_101_?????_0000011")
  val SB      = BitPat("b???????_?????_?????_000_?????_0100011")
  val SH      = BitPat("b???????_?????_?????_001_?????_0100011")
  val SW      = BitPat("b???????_?????_?????_010_?????_0100011")
  val LWU     = BitPat("b???????_?????_?????_110_?????_0000011")
  val LD      = BitPat("b???????_?????_?????_011_?????_0000011")
  val SD      = BitPat("b???????_?????_?????_011_?????_0100011")

   /* CSR */
  val CSRRW   = BitPat("b????????????_?????_001_?????_1110011")
  val CSRRS   = BitPat("b????????????_?????_010_?????_1110011")
  val CSRRC   = BitPat("b????????????_?????_011_?????_1110011")
  val CSRRWI  = BitPat("b????????????_?????_101_?????_1110011")
  val CSRRSI  = BitPat("b????????????_?????_110_?????_1110011")
  val CSRRCI  = BitPat("b????????????_?????_111_?????_1110011")
  val ECALL   = BitPat("b001100000010_00000_000_00000_1110011")
  val MRET    = BitPat("b000000000000_00000_000_00000_1110011")

  val NOP     = BitPat("b000000000000_00000_000_00000_0010011")
  val FENCEI  = BitPat("b000000000000_00000_001_00000_0001111")

  val Mstatus       = 0x300
  val Mie           = 0x304
  val Mtvec         = 0x305
  val Mscratch      = 0x340
  val Mepc          = 0x341
  val Mcause        = 0x342
  val Mip           = 0x344

  val Mcycle        = 0xb00
  val Minstret      = 0xb02
  // val Mcycleh       = 0xb80
  // val Minstreth     = 0xb82

  def privEcall = 0x000.U
  def privEbreak = 0x001.U  
  def privMret  = 0x302.U

  val DecodeDefault = List( InstrN, FuAlu, AluAdd)
  val DecodeTable = Array(
    /*                      Instr |  FU  | FU OP | */
    ADDI           -> List(InstrI, FuAlu, AluAdd),
    SLLI           -> List(InstrI, FuAlu, AluSlli),
    SLTI           -> List(InstrI, FuAlu, AluSlt),    
    SLTIU          -> List(InstrI, FuAlu, AluSltu),
    XORI           -> List(InstrI, FuAlu, AluXor),
    SRLI           -> List(InstrI, FuAlu, AluSrli),
    ANDI           -> List(InstrI, FuAlu, AluAnd),
    SRAI           -> List(InstrI, FuAlu, AluSrai),
    ORI            -> List(InstrI, FuAlu, AluOr ),

    ADD            -> List(InstrR, FuAlu, AluAdd),
    SLL            -> List(InstrR, FuAlu, AluSll),
    SLT            -> List(InstrR, FuAlu, AluSlt),
    SLTU           -> List(InstrR, FuAlu, AluSltu),
    XOR            -> List(InstrR, FuAlu, AluXor),
    SRL            -> List(InstrR, FuAlu, AluSrl),
    OR             -> List(InstrR, FuAlu, AluOr ),
    AND            -> List(InstrR, FuAlu, AluAnd),
    SUB            -> List(InstrR, FuAlu, AluSub),
    SRA            -> List(InstrR, FuAlu, AluSra),

    AUIPC          -> List(InstrU, FuAlu, AluAdd),
    LUI            -> List(InstrU, FuAlu, AluLui),

    ADDIW          -> List(InstrI, FuAlu, AluAddiw),
    SLLIW          -> List(InstrI, FuAlu, AluSllw),
    SRLIW          -> List(InstrI, FuAlu, AluSrlw),
    SRAIW          -> List(InstrI, FuAlu, AluSraw),
    SLLW           -> List(InstrR, FuAlu, AluSllw),
    SRLW           -> List(InstrR, FuAlu, AluSrlw),
    SRAW           -> List(InstrR, FuAlu, AluSraw),
    ADDW           -> List(InstrR, FuAlu, AluAddiw),
    SUBW           -> List(InstrR, FuAlu, AluSubw),

    JAL            -> List(InstrJ, FuBru, BruJal),
    JALR           -> List(InstrI, FuBru, BruJalr),

    BEQ            -> List(InstrB, FuBru, BruBeq),
    BNE            -> List(InstrB, FuBru, BruBne),
    BLT            -> List(InstrB, FuBru, BruBlt),
    BGE            -> List(InstrB, FuBru, BruBge),
    BLTU           -> List(InstrB, FuBru, BruBltu),
    BGEU           -> List(InstrB, FuBru, BruBgeu),

    LB             -> List(InstrI, FuLsu, LsuLb ),
    LH             -> List(InstrI, FuLsu, LsuLh ),
    LW             -> List(InstrI, FuLsu, LsuLw ),
    LD             -> List(InstrI, FuLsu, LsuLd ),
    LBU            -> List(InstrI, FuLsu, LsuLbu),
    LHU            -> List(InstrI, FuLsu, LsuLhu),
    LWU            -> List(InstrI, FuLsu, LsuLwu),
    SB             -> List(InstrS, FuLsu, LsuSb ),
    SH             -> List(InstrS, FuLsu, LsuSh ),
    SW             -> List(InstrS, FuLsu, LsuSw),
    SD             -> List(InstrS, FuLsu, LsuSd),

    CSRRW          -> List(InstrI, FuCsr, CsrW),
    CSRRS          -> List(InstrI, FuCsr, CsrS),
    CSRRC          -> List(InstrI, FuCsr, CsrC),  
    CSRRWI         -> List(InstrI, FuCsr, CsrWi),
    CSRRSI         -> List(InstrI, FuCsr, CsrSi),
    CSRRCI         -> List(InstrI, FuCsr, CsrCi),       
    ECALL          -> List(InstrI, FuCsr, CsrJmp),
    MRET           -> List(InstrI, FuCsr, CsrJmp),
  
    NOP            -> List(InstrI, FuAlu, AluAdd),
    FENCEI         -> List(InstrB, FuMou, Fencei)
  )  
}
